--- @param extension Package
--- @param HY HY_Utility
--- @param U Utility
return function(extension, HY, U)
    ---起手策略：
    ---1.全力抢胜，滚雪球。适用于点数大但质量低的牌型。
    ---2.保手牌，赌败权。适用于防御牌较多的牌型。
    ---3.适当弃牌，均衡运营。适用于有一两张超级大点的牌型。
    ---后续策略：
    ---1.全力进攻，不留后手。适用于有机会秒杀的牌型。
    ---2.靠败权在加速轮翻盘。适用于防御牌较多的牌型。
    ---3.蓄爆+磨血，在加速轮前争取击杀。适用于可以连胜的牌型。

    local desc_flag = [[
  ## 规则简介

  该模式为单挑模式，双方起始手牌数为5，手气卡次数固定为1，无主公技。

  每轮开始前，双方同时弃置至多四张手牌，弃置牌点数和较大的一方为本轮先手方，并获得1点“胜者权”，另一方获得1点“败者权”。

  弃牌之前，双方手牌中的最大点数与最小点数将被广播。

  每有一方以此法弃置了四张或全部（至少一张）手牌，本次双方获得的“胜者权”或“败者权”x2。

  没有弃牌且有手牌/没有手牌的一方从牌堆中获得两张非伤害牌/基本牌。

  若双方点数和相同，则随机决定本轮先后手。

  双方的最小手牌上限为3。

  ---

  ### 胜者权

  一方的出牌阶段开始时，摸与其“胜者权”数值相等的牌。

  ---

  ### 败者权

  每轮结束时，“败者权”最高的一方（相同则随机）可以消耗1至3点“败者权”并执行对应序号项：

  1.随机从牌堆获得一张点数不小于10的牌或一张非基本牌，此牌下轮不会被广播。

  2.摸三张牌，然后失去1点体力。

  3.双方依次受到X点伤害（X为0,2,4中的随机数，从己方开始，双方不会受到相同的伤害）。

  ---

  ### 终结加速

  从第4轮开始，每轮的先手方将连续执行两个回合
]]

    ---@param room Room
    ---@param player ServerPlayer
    ---@param win boolean
    ---@return number
    local function GetRight(room, player, win)
        if player == room.players[1] then
            if win then
                return math.max(tonumber(room:getBanner("p1_win")) or 0, 0)
            else
                return math.max(tonumber(room:getBanner("p1_lose")) or 0, 0)
            end
        else
            if win then
                return math.max(tonumber(room:getBanner("p2_win")) or 0, 0)
            else
                return math.max(tonumber(room:getBanner("p2_lose")) or 0, 0)
            end
        end
    end

    ---@param room Room
    ---@param player ServerPlayer
    ---@param win boolean
    ---@param value number
    local function SetRight(room, player, win, value)
        if player == room.players[1] then
            if win then
                room:setBanner("p1_win", tostring(value))
            else
                room:setBanner("p1_lose", tostring(value))
            end
        else
            if win then
                room:setBanner("p2_win", tostring(value))
            else
                room:setBanner("p2_lose", tostring(value))
            end
        end
        room:setBanner("@p1_info",
            room.players[1].serverplayer:getScreenName() ..
            " : " .. Fk:translate("winner_right") .. " " .. GetRight(room, room.players[1], true) .. " " .. Fk:translate("loser_right") .. " " .. GetRight(room, room.players[1], false))
        room:setBanner("@p2_info",
            room.players[2].serverplayer:getScreenName() ..
            " : " .. Fk:translate("winner_right") .. " " .. GetRight(room, room.players[2], true) .. " " .. Fk:translate("loser_right") .. " " .. GetRight(room, room.players[2], false))
    end

    ---@param room Room
    ---@param player ServerPlayer
    ---@param win boolean
    ---@param value number
    local function ChangeRight(room, player, win, value)
        SetRight(room, player, win, GetRight(room, player, win) + value)
    end

    ---@param room Room
    ---@param log string
    local function Toast(room, log)
        room:sendLog({
            type = log,
            toast = true
        })
    end

    local gambler_mode = fk.CreateGameMode {
        name = "gambler_mode",
        minPlayer = 2,
        maxPlayer = 2,
        logic = function()
            local gambler_logic = GameLogic:subclass("gambler_logic")

            function gambler_logic:run()
                local room = self.room
                room.settings._game.luckTime = 1
                GameLogic.run(self)
            end

            function gambler_logic:chooseGenerals()
                local room = self.room
                local generalNum = room:getSettings('generalNum')
                local n = room:getSettings('enableDeputy') and 2 or 1
                local players = table.simpleClone(room.players)
                room:setCurrent(players[1])

                local generals = table.random(room.general_pile, #players * generalNum)

                local req = Request:new(players, "AskForGeneral")
                for i, p in ipairs(players) do
                    local arg = table.slice(generals, (i - 1) * generalNum + 1, i * generalNum + 1)
                    req:setData(p, { arg, n })
                    req:setDefaultReply(p, table.random(arg, n))
                end

                for _, p in ipairs(players) do
                    local result = req:getResult(p)
                    local general, deputy = result[1], result[2]
                    room:prepareGeneral(p, general, deputy)
                end

                room:askToChooseKingdom(players)
            end

            ---@class Gambler_Round: GameEvent.Round
            local gambler_Round = GameEvent.Round:subclass("Gambler_Round")
            function gambler_Round:action()
                local room = self.room
                local round = room:getBanner("RoundCount")
                local first
                local second
                if room:getTag("first") == 1 then
                    first = room.players[1]
                    second = room.players[2]
                else
                    first = room.players[2]
                    second = room.players[1]
                end
                if round >= 4 then
                    self.data.turn_table = { first, first, second }
                    room:setCurrent(first)
                    Toast(room, Fk:translate("gambler_hint4"))
                    GameEvent.Turn:create(TurnData:new(first)):exec()
                    room:setCurrent(first)
                    Toast(room, Fk:translate("gambler_hint5"))
                    GameEvent.Turn:create(TurnData:new(first)):exec()
                    room:setCurrent(second)
                    Toast(room, Fk:translate("gambler_hint3"))
                    GameEvent.Turn:create(TurnData:new(second)):exec()
                else
                    self.data.turn_table = { first, second }
                    room:setCurrent(first)
                    Toast(room, Fk:translate("gambler_hint2"))
                    GameEvent.Turn:create(TurnData:new(first)):exec()
                    room:setCurrent(second)
                    Toast(room, Fk:translate("gambler_hint3"))
                    GameEvent.Turn:create(TurnData:new(second)):exec()
                end
            end

            function gambler_logic:action()
                self:trigger(fk.GamePrepared)
                local room = self.room
                local p1 = room.players[1]
                local p2 = room.players[2]
                GameEvent.DrawInitial:create():exec()
                SetRight(room, p1, true, 0)
                SetRight(room, p1, false, 0)
                SetRight(room, p2, true, 0)
                SetRight(room, p2, false, 0)
                while true do
                    local origin_p1_cards = p1:getCardIds("h")
                    local origin_p2_cards = p2:getCardIds("h")
                    room:delay(1000)
                    local p1_broadcast = table.except(origin_p1_cards, p1:getTableMark("glamber_broadcast_except"))
                    room:setPlayerMark(p1, "glamber_broadcast_except", {})
                    local p1_max = #p1_broadcast > 0 and math.max(table.unpack(table.map(p1_broadcast, function(id)
                        return Fk:getCardById(id).number
                    end))) or 0
                    local p1_min = #p1_broadcast > 0 and math.min(table.unpack(table.map(p1_broadcast, function(id)
                        return Fk:getCardById(id).number
                    end))) or 0
                    Toast(room, p1.serverplayer:getScreenName() .. Fk:translate("glamber_broadcast1") .. p1_max .. Fk:translate("glamber_broadcast2") .. p1_min)
                    room:delay(2500)
                    local p2_broadcast = table.except(origin_p2_cards, p2:getTableMark("glamber_broadcast_except"))
                    room:setPlayerMark(p2, "glamber_broadcast_except", {})
                    local p2_max = #p2_broadcast > 0 and math.max(table.unpack(table.map(p2_broadcast, function(id)
                        return Fk:getCardById(id).number
                    end))) or 0
                    local p2_min = #p2_broadcast > 0 and math.min(table.unpack(table.map(p2_broadcast, function(id)
                        return Fk:getCardById(id).number
                    end))) or 0
                    Toast(room, p2.serverplayer:getScreenName() .. Fk:translate("glamber_broadcast1") .. p2_max .. Fk:translate("glamber_broadcast2") .. p2_min)
                    room:delay(2500)
                    ---@type AskToDiscardParams
                    local params = {
                        min_num = 1,
                        max_num = 4,
                        skill_name = "gambler_discard",
                        prompt = "gambler_discard_prompt"
                    }
                    local result = HY.AskToDiscardSameTime(room, { { p1, params }, { p2, params } })
                    local p1_cards = result[p1]
                    local p2_cards = result[p2]
                    local value = 1
                    if #p1_cards == 4 or (#p1_cards == #origin_p1_cards and #p1_cards > 0) then
                        value = value * 2
                    end
                    if #p2_cards == 4 or (#p2_cards == #origin_p2_cards and #p2_cards > 0) then
                        value = value * 2
                    end
                    local p1_total = table.sum(p1_cards, function(id)
                        return Fk:getCardById(id).number
                    end)
                    local p2_total = table.sum(p2_cards, function(id)
                        return Fk:getCardById(id).number
                    end)
                    Toast(room, p1.serverplayer:getScreenName() .. " - " .. p1_total .. " vs. " .. p2_total .. " - " .. p2.serverplayer:getScreenName())
                    room:delay(1000)
                    if p1_total > p2_total then
                        room:setTag("first", 1)
                        ChangeRight(room, p1, true, value)
                        ChangeRight(room, p2, false, value)
                        Toast(room, p1.serverplayer:getScreenName() .. Fk:translate("gambler_discard_winner"))
                    elseif p2_total > p1_total then
                        room:setTag("first", 2)
                        ChangeRight(room, p2, true, value)
                        ChangeRight(room, p1, false, value)
                        Toast(room, p2.serverplayer:getScreenName() .. Fk:translate("gambler_discard_winner"))
                    else
                        local result = math.random(1, 2)
                        if result == 1 then
                            room:setTag("first", 1)
                            Toast(room, p1.serverplayer:getScreenName() .. Fk:translate("gambler_discard_winner"))
                        else
                            room:setTag("first", 2)
                            Toast(room, p2.serverplayer:getScreenName() .. Fk:translate("gambler_discard_winner"))
                        end
                    end
                    room:delay(1000)
                    local cards = table.clone(room.draw_pile)
                    local gets = {} ---@type CardsMoveInfo[]
                    local ids = {}
                    if #p1_cards == 0 then
                        if #origin_p1_cards > 0 then
                            ids = table.random(table.filter(cards, function(id)
                                return not Fk:getCardById(id).is_damage_card
                            end), 2)
                        else
                            ids = table.random(table.filter(cards, function(id)
                                return Fk:getCardById(id).type == Card.TypeBasic
                            end), 2)
                        end
                        table.insertIfNeed(gets, {
                            to = p1,
                            toArea = Card.PlayerHand,
                            ids = ids,
                            moveReason = fk.ReasonPrey,
                            skillName = "glamber_defence",
                            moveVisible = false,
                        })
                        for _, id in ipairs(ids) do
                            table.removeOne(cards, id)
                        end
                    end
                    if #p2_cards == 0 then
                        if #origin_p2_cards > 0 then
                            ids = table.random(table.filter(cards, function(id)
                                return not Fk:getCardById(id).is_damage_card
                            end), 2)
                        else
                            ids = table.random(table.filter(cards, function(id)
                                return Fk:getCardById(id).type == Card.TypeBasic
                            end), 2)
                        end
                        table.insertIfNeed(gets, {
                            to = p2,
                            toArea = Card.PlayerHand,
                            ids = ids,
                            moveReason = fk.ReasonPrey,
                            skillName = "glamber_defence",
                            moveVisible = false,
                        })
                        for _, id in ipairs(ids) do
                            table.removeOne(cards, id)
                        end
                    end
                    if #gets > 0 then
                        room:moveCards(table.unpack(gets))
                    end
                    gambler_Round:create():exec()
                    if room.game_finished then break end
                    if table.every(room.players, function(p) return p.dead and p.rest == 0 end) then room:gameOver("") end
                end
            end

            return gambler_logic
        end,
        rule = "gambler_rule",
        surrender_func = function(self, playedTime)
            local room = Fk:currentRoom()
            if Self == room.players[1] then
                return { { text = "gambler_surrender", passed = GetRight(room, room.players[2], true) >= 3 } }
            else
                return { { text = "gambler_surrender", passed = GetRight(room, room.players[1], true) >= 3 } }
            end
        end,
    }

    local gambler_rule = fk.CreateSkill {
        name = "gambler_rule"
    }
    gambler_rule:addEffect(fk.DrawInitialCards, {
        priority = 0,
        can_refresh = function(self, event, target, player, data)
            return target == player
        end,
        on_refresh = function(self, event, target, player, data)
            data.num = 5
        end
    })
    gambler_rule:addEffect("maxcards", {
        priority = 0,
        fixed_func = function(self, player)
            if player.hp then
                local result = math.max(player.hp, 0)
                local status_skills = Fk:currentRoom().status_skills[MaxCardsSkill] or Util.DummyTable
                local max_fixed = nil
                for _, skill in ipairs(status_skills) do
                    if skill ~= self then
                        local f = skill:getFixed(player)
                        if f ~= nil then
                            max_fixed = max_fixed and math.max(max_fixed, f) or f
                        end
                    end
                end
                if max_fixed then result = math.max(max_fixed, 0) end
                return math.max(3, result)
            end
        end
    })
    gambler_rule:addEffect(fk.EventPhaseStart, {
        priority = 0,
        can_refresh = function(self, event, target, player, data)
            return target == player and player.phase == Player.Play
        end,
        on_refresh = function(self, event, target, player, data)
            local room = player.room
            room:drawCards(player, GetRight(room, player, true), "gambler_winner")
        end
    })
    gambler_rule:addEffect(fk.RoundEnd, {
        priority = 0,
        late_refresh = true,
        can_refresh = function(self, event, target, player, data)
            return player == player.room.current
        end,
        on_refresh = function(self, event, target, player, data)
            local room = player.room
            if room:getBanner("RoundCount") == 3 then
                Toast(room, Fk:translate("gambler_hint1"))
            end
            local p1_lose = GetRight(room, room.players[1], false)
            local p2_lose = GetRight(room, room.players[2], false)
            if p1_lose <= 0 and p2_lose <= 0 then
                return
            end
            local invoker
            if p1_lose > p2_lose then
                invoker = room.players[1]
            elseif p2_lose > p1_lose then
                invoker = room.players[2]
            else
                invoker = room.players[math.random(1, 2)]
            end
            local loser_rights = GetRight(room, invoker, false)
            local choices = {}
            for i = 1, loser_rights, 1 do
                table.insertIfNeed(choices, "gambler_loser" .. i)
            end
            table.insertIfNeed(choices, "Cancel")
            local result = room:askToChoice(invoker, {
                choices = choices,
                skill_name = "gambler_loser",
                detailed = true,
            })
            if result == "gambler_loser1" then
                ChangeRight(room, invoker, false, -1)
                local except = invoker:getTableMark("glamber_broadcast_except")
                local gain
                if math.random(1, 2) == 1 then
                    local cards = table.filter(room.draw_pile, function(id)
                        return Fk:getCardById(id).number >= 10
                    end)
                    gain = table.random(cards, 1)
                else
                    local cards = table.filter(room.draw_pile, function(id)
                        return Fk:getCardById(id).type ~= Card.TypeBasic
                    end)
                    gain = table.random(cards, 1)
                end
                if #gain > 0 then
                    room:moveCardTo(gain, Card.PlayerHand, invoker, fk.ReasonPrey, "gambler_loser", nil, false)
                    table.insertTableIfNeed(except, gain)
                    room:setPlayerMark(invoker, "glamber_broadcast_except", except)
                end
            elseif result == "gambler_loser2" then
                ChangeRight(room, invoker, false, -2)
                room:drawCards(invoker, 3, "gambler_loser")
                room:loseHp(invoker, 1, "gambler_loser")
            elseif result == "gambler_loser3" then
                ChangeRight(room, invoker, false, -3)
                local first = invoker
                local second = invoker == room.players[1] and room.players[2] or room.players[1]
                local damages = { 0, 2, 4 }
                local damage1 = table.random(damages, 1)[1]
                table.removeOne(damages, damage1)
                local damage2 = table.random(damages, 1)[1]
                Toast(room, first.serverplayer:getScreenName() .. Fk:translate("gambler_hint6"))
                room:delay(2500)
                Toast(room, damage1 .. Fk:translate("gambler_hint7"))
                room:damage({
                    to = first,
                    damage = damage1,
                    skillName = "gambler_loser"
                })
                if not first.dead then
                    room:delay(1500)
                    Toast(room, second.serverplayer:getScreenName() .. Fk:translate("gambler_hint6"))
                    room:delay(2500)
                    Toast(room, damage2 .. Fk:translate("gambler_hint7"))
                    room:damage({
                        to = second,
                        damage = damage2,
                        skillName = "gambler_loser"
                    })
                end
            end
        end
    })

    extension:loadSkillSkels { gambler_rule }
    extension:addGameMode(gambler_mode)

    Fk:loadTranslationTable {
        ["touhou_gambler"] = "游戏模式",
        ["gambler_mode"] = "博弈对决",
        [":gambler_mode"] = desc_flag,
        ["gambler_discard"] = "投资下注",
        ["gambler_discard_prompt"] = "用至多四张牌的点数和争夺先手权！",
        ["gambler_discard_winner"] = "获得了本轮的先手权！",
        ["gambler_winner"] = "赢家通吃",
        ["@p1_info"] = "",
        ["@p2_info"] = "",
        ["winner_right"] = "胜",
        ["loser_right"] = "败",
        ["gambler_loser"] = "逆境一搏",
        ["gambler_loser1"] = "1",
        [":gambler_loser1"] = "随机从牌堆获得一张点数不小于10的牌或一张非基本牌",
        ["gambler_loser2"] = "2",
        [":gambler_loser2"] = "摸三张牌，然后失去1点体力",
        ["gambler_loser3"] = "3",
        [":gambler_loser3"] = "双方依次受到X点伤害（X为0,2,4中的随机数，从己方开始，双方不会受到相同的伤害）",
        ["gambler_surrender"] = "对方胜者权不小于3",
        ["glamber_defence"] = "弃卒保帅",
        ["glamber_broadcast1"] = " 手牌中的最大点为：",
        ["glamber_broadcast2"] = "，最小点为：",
        ["gambler_hint1"] = "下一轮开始将进入终结加速！",
        ["gambler_hint2"] = "当前为：先手回合",
        ["gambler_hint3"] = "当前为：后手回合",
        ["gambler_hint4"] = "当前为：先手第一回合",
        ["gambler_hint5"] = "当前为：先手第二回合",
        ["gambler_hint6"] = "将受到……",
        ["gambler_hint7"] = "点伤害！",
    }
end
